import ioredis from 'ioredis';

import type { Next } from 'koa';
import type { KoaContext } from '@/types/koa';

import {
  FIND_USER_ERROR,
} from '@/constant/error/server.error';
import {
  EMAIL_EMPTY_ERROR,
  EMAIL_FORMAT_ERROR,
  ACCOUNT_EMPTY_ERROR,
  ACCOUNT_FORMAT_ERROR,
  VERIFY_CODE_ERROR,
  PASSWORD_ERROR,
} from '@/constant/error/validator.error';

// ? 校验邮箱格式
export const checkEmailFormat = async (ctx: KoaContext, next: Next) => {
  const { email } = ctx.request.body;

  if (!email?.trim()) {
    return ctx.app.emit('error', EMAIL_EMPTY_ERROR, ctx);
  }

  if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(email.trim())) {
    return ctx.app.emit('error', EMAIL_FORMAT_ERROR, ctx);
  }

  await next();
};

// todo 校验手机号格式
export const checkPhoneFormat = async (ctx: KoaContext, next: Next) => {
  await next();
};

// ? 校验账号格式
export const checkAccountFormat = async (ctx: KoaContext, next: Next) => {
  const { account } = ctx.request.body;

  if (!account?.trim()) {
    return ctx.app.emit('error', ACCOUNT_EMPTY_ERROR, ctx);
  }

  if (!/^[\d]{9}$/.test(account.trim())) {
    return ctx.app.emit('error', ACCOUNT_FORMAT_ERROR, ctx);
  }

  await next();
};

// ? 校验验证码格式
export const checkVerifyCodeFormat = ({
  type,
  digits = 6,
}: {
  type: 'number' | 'string',
  digits?: number,
}) => async (ctx: KoaContext, next: Next) => {
  const { code } = ctx.request.body;

  if (!code?.trim()) {
    return ctx.app.emit('error', {
      code: 400,
      message: '验证码不能为空',
    }, ctx);
  }

  switch (type) {
    case 'number':
      const numberRegexp = new RegExp(`^[\\d]{${digits}}$`);
      if (!numberRegexp.test(code.trim())) {
        return ctx.app.emit('error', {
          code: 400,
          message: `请输入${digits}位的纯数字验证码`,
        }, ctx);
      }
      break;
    case 'string':
      const stringRegexp = new RegExp(`^[\\d\\w\\W]{${digits}}$`);
      if (!stringRegexp.test(code.trim())) {
        return ctx.app.emit('error', {
          code: 400,
          message: `请输入${digits}位的验证码`,
        }, ctx);
      }
      break;
    default:
      console.warn('warning: unknown type');
      break;
  }
  await next();
};

// ? 校验密码是否为空
export const checkPasswordIsEmpty = async (ctx: KoaContext, next: Next) => {
  const { password } = ctx.request.body;

  if (!password?.trim()) {
    return ctx.app.emit('error', { code: 400, message: '密码不能为空' }, ctx);
  }

  await next();
};

// ? 验证密码是否正确
export const validationPasswordIsCorrect = async (ctx: KoaContext, next: Next) => {
  const { password } = ctx.request.body;
  const { user_db_password } = ctx.cache;

  if (user_db_password !== password) {
    return ctx.app.emit('error', PASSWORD_ERROR, ctx);
  }

  await next();
};

// ? 验证用户是否已存在
export const validationUserIsExist = ({
  service,
  key,
  cache,
}: {
  service: Function,
  key: 'email' | 'phone' | 'account',
  cache?: boolean,
}) => async (ctx: KoaContext, next: Next) => {
  type WhereKey = {
    email?: string,
    phone?: string,
    account?: string,
  }
  const where: WhereKey = {};
  where[key] = ctx.request.body[key];

  // const attributes = ['id', 'password'];

  try {
    const res = await service({ where });
    ctx.state.userIsExist = res ? true : false;
    if (res && cache) {
      const { password, ...user } = res['dataValues'];
      ctx.cache['user_db_password'] = password;
      ctx.state.user = user;
    }
  } catch (error) {
    return ctx.app.emit('error', { ...FIND_USER_ERROR, error }, ctx);
  }

  await next();
};

// ? 验证验证码是否过期
export const validationVerifyCodeIsValidity = ({
  redisDB,
  prefix,
  key,
}: {
  redisDB: ioredis,
  prefix: string,
  key: 'email' | 'phone' |'account',
}) => async (ctx: KoaContext, next: Next) => {
  const time = await redisDB.ttl(prefix + ctx.request.body[key]);

  ctx.state.verifyCodeIsValidity = time >= 0 ? true : false;
  ctx.state.verifyCodeCountDown = time;

  await next();
};
// ? 验证验证码是否正确
export const validateVerifyCode = ({
  redisDB,
  prefix,
  key,
}: {
  redisDB: ioredis,
  prefix: string,
  key: 'email' | 'phone' |'account',
}) => async (ctx: KoaContext, next: Next) => {
  const { code } = ctx.request.body;

  const redisCode = await redisDB.get(prefix + ctx.request.body[key]);
  if (redisCode !== code) {
    return ctx.app.emit('error', VERIFY_CODE_ERROR, ctx);
  }

  await next();
};

// ? 验证状态拦截
export const validateStateIntercept = ({
  state,
  validateKey,
  message,
}: {
  state: boolean,
  validateKey: 'userIsExist' | 'verifyCodeIsValidity',
  message: any,
}) => async (ctx: KoaContext, next: Next) => {
  if (state === ctx.state[validateKey]) {
    if (validateKey === 'verifyCodeIsValidity' && ctx.state[validateKey] === true) {
      message['countdown'] = ctx.state.verifyCodeCountDown as number >= 0 ? ctx.state.verifyCodeCountDown : 0;
    }

    return ctx.app.emit('error', message, ctx);
  }

  await next();
};
